home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 5 / Skunkware 5.iso / src / Tools / ghostview-1.5 / Ghostview.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-05-03  |  60.8 KB  |  2,016 lines

  1. /*
  2.  * Ghostview.c -- Ghostview widget.
  3.  * Copyright (C) 1992  Timothy O. Theisen
  4.  *
  5.  * This program is free software; you can redistribute it and/or modify
  6.  * it under the terms of the GNU General Public License as published by
  7.  * the Free Software Foundation; either version 2 of the License, or
  8.  * (at your option) any later version.
  9.  *
  10.  * This program is distributed in the hope that it will be useful,
  11.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13.  * GNU General Public License for more details.
  14.  *
  15.  * You should have received a copy of the GNU General Public License
  16.  * along with this program; if not, write to the Free Software
  17.  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  18.  *
  19.  *   Author: Tim Theisen           Systems Programmer
  20.  * Internet: tim@cs.wisc.edu       Department of Computer Sciences
  21.  *     UUCP: uwvax!tim             University of Wisconsin-Madison
  22.  *    Phone: (608)262-0438         1210 West Dayton Street
  23.  *      FAX: (608)262-9777         Madison, WI   53706
  24.  */
  25.  
  26. #include <X11/IntrinsicP.h>
  27. #include <X11/StringDefs.h>
  28. #include <X11/Xatom.h>
  29. #include <X11/Xproto.h>
  30. #include <X11/Xos.h>
  31. #include "GhostviewP.h"
  32. #include <ctype.h>
  33.  
  34. #ifndef XlibSpecificationRelease
  35. typedef char *XPointer;
  36. #endif
  37.  
  38. #include <signal.h>
  39. #ifdef SIGNALRETURNSINT
  40. #define SIGVAL int
  41. #else
  42. #define SIGVAL void
  43. #endif
  44.  
  45. #ifdef NON_BLOCKING_IO
  46. #include <fcntl.h>
  47. /* if POSIX O_NONBLOCK is not available, use O_NDELAY */
  48. #if !defined(O_NONBLOCK) && defined(O_NDELAY)
  49. #define O_NONBLOCK O_NDELAY
  50. #endif
  51. #endif
  52.  
  53. #include <errno.h>
  54. /* BSD 4.3 errno.h does not declare errno */
  55. extern int errno;
  56. /* Both error returns are checked for non-blocking I/O. */
  57. /* Manufacture the other error code if only one exists. */
  58. #if !defined(EWOULDBLOCK) && defined(EAGAIN)
  59. #define EWOULDBLOCK EAGAIN
  60. #endif
  61. #if !defined(EAGAIN) && defined(EWOULDBLOCK)
  62. #define EAGAIN EWOULDBLOCK
  63. #endif
  64.  
  65. #ifndef VMS
  66. /* GV_BUFSIZ is set to the minimum POSIX PIPE_BUF to ensure that
  67.  * nonblocking writes to ghostscript will work properly.
  68.  */
  69. #define GV_BUFSIZ 512
  70. #else /* VMS */
  71. /*
  72. ** GV_BUFSIZ is the maximum length line we can handle, so we up it to 1024
  73. */
  74. #define GV_BUFSIZ 1024
  75. #endif /* VMS */
  76.  
  77. static void ComputeXdpi();
  78. static void ComputeYdpi();
  79.  
  80. static XtResource resources[] = {
  81. #define offset(field) XtOffsetOf(GhostviewRec, ghostview.field)
  82.     { XtNarguments, XtCArguments, XtRString, sizeof(String),
  83.       offset(arguments), XtRString, (XtPointer)NULL },
  84.     { XtNbottomMargin, XtCMargin, XtRInt, sizeof(int),
  85.       offset(bottom_margin), XtRImmediate, (XtPointer)0 },
  86.     { XtNbusyCursor, XtCCursor, XtRCursor, sizeof(XtPointer),
  87.       offset(busy_cursor), XtRString, "watch" },
  88.     { XtNcallback, XtCCallback, XtRCallback, sizeof(XtPointer),
  89.       offset(callback), XtRCallback, (XtPointer)NULL },
  90.     { XtNcursor, XtCCursor, XtRCursor, sizeof(XtPointer),
  91.       offset(cursor), XtRString, "crosshair" },
  92.     { XtNfilename, XtCFilename, XtRString, sizeof(String),
  93.       offset(filename), XtRString, (XtPointer)NULL },
  94.     { XtNforeground, XtCForeground, XtRPixel, sizeof(Pixel),
  95.       offset(foreground), XtRString, XtDefaultForeground},
  96.     { XtNinterpreter, XtCInterpreter, XtRString, sizeof(String),
  97.       offset(interpreter), XtRString, "gs" },
  98.     { XtNleftMargin, XtCMargin, XtRInt, sizeof(int),
  99.       offset(left_margin), XtRImmediate, (XtPointer)0 },
  100.     { XtNllx, XtCBoundingBox, XtRInt, sizeof(int),
  101.       offset(llx), XtRImmediate, (XtPointer)0 },
  102.     { XtNlly, XtCBoundingBox, XtRInt, sizeof(int),
  103.       offset(lly), XtRImmediate, (XtPointer)0 },
  104.     { XtNmessageCallback, XtCCallback, XtRCallback, sizeof(XtPointer),
  105.       offset(message_callback), XtRCallback, (XtPointer)NULL },
  106.     { XtNorientation, XtCOrientation, XtRPageOrientation,
  107.       sizeof(XtPageOrientation), offset(orientation), XtRImmediate,
  108.       (XtPointer)XtPageOrientationPortrait },
  109.     { XtNoutputCallback, XtCCallback, XtRCallback, sizeof(XtPointer),
  110.       offset(output_callback), XtRCallback, (XtPointer)NULL },
  111.     { XtNpalette, XtCPalette, XtRPalette, sizeof(XtPalette),
  112.       offset(palette), XtRImmediate, (XtPointer)XtPaletteColor },
  113.     { XtNquiet, XtCQuiet, XtRBoolean, sizeof(Boolean),
  114.       offset(quiet), XtRImmediate, (XtPointer)True },
  115.     { XtNrightMargin, XtCMargin, XtRInt, sizeof(int),
  116.       offset(right_margin), XtRImmediate, (XtPointer)0 },
  117.     { XtNsafer, XtCSafer, XtRBoolean, sizeof(Boolean),
  118.       offset(safer), XtRImmediate, (XtPointer)True },
  119.     { XtNtopMargin, XtCMargin, XtRInt, sizeof(int),
  120.       offset(top_margin), XtRImmediate, (XtPointer)0 },
  121.     { XtNuseBackingPixmap, XtCUseBackingPixmap, XtRBoolean, sizeof(Boolean),
  122.       offset(use_bpixmap), XtRImmediate, (XtPointer)True },
  123.     { XtNurx, XtCBoundingBox, XtRInt, sizeof(int),
  124.       offset(urx), XtRImmediate, (XtPointer)612 },
  125.     { XtNury, XtCBoundingBox, XtRInt, sizeof(int),
  126.       offset(ury), XtRImmediate, (XtPointer)792 },
  127.     { XtNxdpi, XtCResolution, XtRFloat, sizeof(float),
  128.       offset(xdpi), XtRCallProc, (XtPointer)ComputeXdpi },
  129.     { XtNydpi, XtCResolution, XtRFloat, sizeof(float),
  130.       offset(ydpi), XtRCallProc, (XtPointer)ComputeYdpi },
  131. #undef offset
  132. };
  133.  
  134. static void Message();
  135. static void Notify();
  136. static void Input();
  137. static void Output();
  138.  
  139. static void ClassInitialize();
  140. static void ClassPartInitialize();
  141. static void Initialize();
  142. static void Realize();
  143. static void Destroy();
  144. static void Resize();
  145. static Boolean SetValues();
  146. static XtGeometryResult QueryGeometry();
  147.  
  148. static void Layout();
  149. static Boolean ComputeSize();
  150. static void ChangeSize();
  151. static Boolean Setup();
  152. static void StartInterpreter();
  153. static void StopInterpreter();
  154. static void InterpreterFailed();
  155.  
  156. static XtActionsRec actions[] =
  157. {
  158.     {"message",    Message},
  159.     {"notify",    Notify},
  160. };
  161.  
  162. /* notify takes zero to four parameters.  The first two give the width and
  163.  * height of the zoom requested in the default user coordinate system.
  164.  * If they are omitted, a default value of 72 is provided.  If the second
  165.  * parameter is omitted, the zoom area is assumed to be a square.
  166.  * The next two parameters give the desired resolution of the zoom window.
  167.  * If they are omitted, a default value of 300 is provided. If the four
  168.  * parameter is omitted, the y resolution is assumed to be equal to the
  169.  * x resolution.
  170.  */
  171. static char translations[] =
  172. "<Message>:        message()    \n\
  173. <Btn1Down>:        notify(72)    \n\
  174. <Btn2Down>:        notify(108)    \n\
  175. <Btn3Down>:        notify(144)    \n\
  176. ";
  177.  
  178. GhostviewClassRec ghostviewClassRec = {
  179.   { /* core fields */
  180.     /* superclass        */    (WidgetClass) &coreClassRec,
  181.     /* class_name        */    "Ghostview",
  182.     /* widget_size        */    sizeof(GhostviewRec),
  183.     /* class_initialize        */    ClassInitialize,
  184.     /* class_part_initialize    */    ClassPartInitialize,
  185.     /* class_inited        */    FALSE,
  186.     /* initialize        */    Initialize,
  187.     /* initialize_hook        */    NULL,
  188.     /* realize            */    Realize,
  189.     /* actions            */    actions,
  190.     /* num_actions        */    XtNumber(actions),
  191.     /* resources        */    resources,
  192.     /* num_resources        */    XtNumber(resources),
  193.     /* xrm_class        */    NULLQUARK,
  194.     /* compress_motion        */    TRUE,
  195.     /* compress_exposure    */    TRUE,
  196.     /* compress_enterleave    */    TRUE,
  197.     /* visible_interest        */    FALSE,
  198.     /* destroy            */    Destroy,
  199.     /* resize            */    Resize,
  200.     /* expose            */    NULL,
  201.     /* set_values        */    SetValues,
  202.     /* set_values_hook        */    NULL,
  203.     /* set_values_almost    */    XtInheritSetValuesAlmost,
  204.     /* get_values_hook        */    NULL,
  205.     /* accept_focus        */    NULL,
  206.     /* version            */    XtVersion,
  207.     /* callback_private        */    NULL,
  208.     /* tm_table            */    translations,
  209.     /* query_geometry        */    QueryGeometry,
  210.     /* display_accelerator    */    XtInheritDisplayAccelerator,
  211.     /* extension        */    NULL
  212.   },
  213.   { /* ghostview fields */
  214.     /* ghostview        */    NULL,
  215.     /* gv_colors        */    NULL,
  216.     /* next            */    NULL,
  217.     /* page            */    NULL,
  218.     /* done            */    NULL
  219.   }
  220. };
  221.  
  222. WidgetClass ghostviewWidgetClass = (WidgetClass)&ghostviewClassRec;
  223.  
  224. /* Procedures that compute the default xdpi and ydpi from display parameters */
  225.  
  226. static void
  227. ComputeXdpi(w, offset, value)
  228.     Widget w;
  229.     int offset;
  230.     XrmValue *value;
  231. {
  232.     static float xdpi;
  233.     xdpi = 25.4 * WidthOfScreen(XtScreen(w)) / WidthMMOfScreen(XtScreen(w));
  234.     value->addr = (XtPointer) &xdpi;
  235. }
  236.  
  237. static void
  238. ComputeYdpi(w, offset, value)
  239.     Widget w;
  240.     int offset;
  241.     XrmValue *value;
  242. {
  243.     static float ydpi;
  244.     ydpi = 25.4 * HeightOfScreen(XtScreen(w)) / HeightMMOfScreen(XtScreen(w));
  245.     value->addr = (XtPointer) &ydpi;
  246. }
  247.  
  248. /* Message action routine.
  249.  * Passes ghostscript message events back to application via
  250.  * the message callback.  It also marks the interpreter as
  251.  * being not busy at the end of page, and stops the interpreter
  252.  * when it send a "done" message.
  253.  */
  254. static void
  255. Message(w, event, params, num_params)
  256.     Widget w;
  257.     XEvent *event;
  258.     String *params;        /* unused */
  259.     Cardinal *num_params;    /* unused */
  260. {
  261.     GhostviewWidget gvw = (GhostviewWidget) w;
  262.     GhostviewWidgetClass gvc = (GhostviewWidgetClass) XtClass(w);
  263.  
  264.     gvw->ghostview.mwin = event->xclient.data.l[0];
  265.     if (event->xclient.message_type ==
  266.     XmuInternAtom(XtDisplay(w), gvc->ghostview_class.page)) {
  267.     gvw->ghostview.busy = False;
  268.     XDefineCursor(XtDisplay(gvw), XtWindow(gvw), gvw->ghostview.cursor);
  269.     XtCallCallbackList(w, gvw->ghostview.message_callback, "Page");
  270.     } else if (event->xclient.message_type ==
  271.            XmuInternAtom(XtDisplay(w), gvc->ghostview_class.done)) {
  272.     StopInterpreter(w);
  273.     XtCallCallbackList(w, gvw->ghostview.message_callback, "Done");
  274.     }
  275. }
  276.  
  277. /* Notify action routine.
  278.  * Calculates where the user clicked in the default user coordinate system.
  279.  * Call the callbacks with the point of click and size of zoom window
  280.  * requested.
  281.  */
  282. static void
  283. Notify(w, event, params, num_params)
  284.     Widget w;
  285.     XEvent *event;
  286.     String *params;
  287.     Cardinal *num_params;
  288. {
  289.     GhostviewWidget gvw = (GhostviewWidget) w;
  290.     GhostviewReturnStruct ret_val;
  291.  
  292.     /* notify takes zero to four parameters.  The first two give the width and
  293.      * height of the zoom requested in the default user coordinate system.
  294.      * If they are omitted, a default value of 72 is provided.  If the second
  295.      * parameter is omitted, the zoom area is assumed to be a square.
  296.      * The next two parameters give the desired resolution of the zoom window.
  297.      * If they are omitted, a default value of 300 is provided. If the four
  298.      * parameter is omitted, the y resolution is assumed to be equal to the
  299.      * x resolution.
  300.      */
  301.     switch (*num_params) {
  302.     case 0:
  303.     ret_val.width = ret_val.height = 72;
  304.     ret_val.xdpi = ret_val.ydpi = 300;
  305.     break;
  306.     case 1:
  307.     ret_val.width = ret_val.height = atoi(params[0]);
  308.     ret_val.xdpi = ret_val.ydpi = 300;
  309.     break;
  310.     case 2:
  311.     ret_val.width = atoi(params[0]);
  312.     ret_val.height = atoi(params[1]);
  313.     ret_val.xdpi = ret_val.ydpi = 300;
  314.     break;
  315.     case 3:
  316.     ret_val.width = atoi(params[0]);
  317.     ret_val.height = atoi(params[1]);
  318.     ret_val.xdpi = ret_val.ydpi = atoi(params[2]);
  319.     break;
  320.     default:
  321.     ret_val.width = atoi(params[0]);
  322.     ret_val.height = atoi(params[1]);
  323.     ret_val.xdpi = atoi(params[2]);
  324.     ret_val.ydpi = atoi(params[3]);
  325.     break;
  326.     }
  327.  
  328.     switch (gvw->ghostview.orientation) {
  329.     case XtPageOrientationPortrait:
  330.     ret_val.psx = gvw->ghostview.llx + 
  331.               event->xbutton.x * 72.0 / gvw->ghostview.xdpi;
  332.     ret_val.psy = gvw->ghostview.ury - 
  333.               event->xbutton.y * 72.0 / gvw->ghostview.ydpi;
  334.     break;
  335.     case XtPageOrientationLandscape:
  336.     ret_val.psx = gvw->ghostview.llx + 
  337.               event->xbutton.y * 72.0 / gvw->ghostview.ydpi;
  338.     ret_val.psy = gvw->ghostview.lly + 
  339.               event->xbutton.x * 72.0 / gvw->ghostview.xdpi;
  340.     break;
  341.     case XtPageOrientationUpsideDown:
  342.     ret_val.psx = gvw->ghostview.urx - 
  343.               event->xbutton.x * 72.0 / gvw->ghostview.xdpi;
  344.     ret_val.psy = gvw->ghostview.lly + 
  345.               event->xbutton.y * 72.0 / gvw->ghostview.ydpi;
  346.     break;
  347.     case XtPageOrientationSeascape:
  348.     ret_val.psx = gvw->ghostview.urx - 
  349.               event->xbutton.y * 72.0 / gvw->ghostview.ydpi;
  350.     ret_val.psy = gvw->ghostview.ury - 
  351.               event->xbutton.x * 72.0 / gvw->ghostview.xdpi;
  352.     break;
  353.     }
  354.     XtCallCallbackList(w, gvw->ghostview.callback, (XtPointer) &ret_val);
  355. }
  356.  
  357. #ifndef SEEK_SET
  358. #define SEEK_SET 0
  359. #endif
  360.  
  361. static Boolean broken_pipe = False;
  362.  
  363. static SIGVAL
  364. CatchPipe(i)
  365.     int i;
  366. {
  367.     broken_pipe = True;
  368. #ifdef SIGNALRETURNSINT
  369.     return 0;
  370. #endif
  371. }
  372.  
  373. #ifndef VMS
  374.  
  375. /* Input - Feed data to ghostscript's stdin.
  376.  * Write bytes to ghostscript using non-blocking I/O.
  377.  * Also, pipe signals are caught during writing.  The return
  378.  * values are checked and the appropriate action is taken.  I do
  379.  * this at this low level, because it may not be appropriate for
  380.  * SIGPIPE to be caught for the overall application.
  381.  */
  382.  
  383. static void
  384. Input(client_data, source, id)
  385.     XtPointer client_data;
  386.     int *source;
  387.     XtInputId *id;
  388. {
  389.     Widget w = (Widget) client_data;
  390.     GhostviewWidget gvw = (GhostviewWidget) w;
  391.     int bytes_written;
  392.     SIGVAL (*oldsig)();
  393.  
  394.     oldsig = signal(SIGPIPE, CatchPipe);
  395.  
  396. #ifdef NON_BLOCKING_IO
  397.     do {
  398. #endif
  399.  
  400.     if (gvw->ghostview.buffer_bytes_left == 0) {
  401.  
  402.         /* Get a new section if required */
  403.         if (gvw->ghostview.ps_input && gvw->ghostview.bytes_left == 0) {
  404.         struct record_list *ps_old = gvw->ghostview.ps_input;
  405.         gvw->ghostview.ps_input = ps_old->next;
  406.         if (ps_old->close) fclose(ps_old->fp);
  407.         XtFree((char *)ps_old);
  408.         }
  409.  
  410.         /* Have to seek at the beginning of each section */
  411.         if (gvw->ghostview.ps_input &&
  412.         gvw->ghostview.ps_input->seek_needed) {
  413.         if (gvw->ghostview.ps_input->len > 0)
  414.             fseek(gvw->ghostview.ps_input->fp,
  415.               gvw->ghostview.ps_input->begin, SEEK_SET);
  416.         gvw->ghostview.ps_input->seek_needed = False;
  417.         gvw->ghostview.bytes_left = gvw->ghostview.ps_input->len;
  418.         }
  419.  
  420.         if (gvw->ghostview.bytes_left > GV_BUFSIZ) {
  421.         gvw->ghostview.buffer_bytes_left =
  422.             fread(gvw->ghostview.input_buffer,
  423.                   sizeof (char), GV_BUFSIZ,
  424.                   gvw->ghostview.ps_input->fp);
  425.         } else if (gvw->ghostview.bytes_left > 0) {
  426.         gvw->ghostview.buffer_bytes_left =
  427.             fread(gvw->ghostview.input_buffer,
  428.                   sizeof (char), gvw->ghostview.bytes_left,
  429.                   gvw->ghostview.ps_input->fp);
  430.         } else {
  431.         gvw->ghostview.buffer_bytes_left = 0;
  432.         }
  433.         if (gvw->ghostview.bytes_left > 0 &&
  434.         gvw->ghostview.buffer_bytes_left == 0) {
  435.         InterpreterFailed(w);    /* Error occurred */
  436.         }
  437.         gvw->ghostview.input_buffer_ptr = gvw->ghostview.input_buffer;
  438.         gvw->ghostview.bytes_left -= gvw->ghostview.buffer_bytes_left;
  439.     }
  440.  
  441.     if (gvw->ghostview.buffer_bytes_left > 0) {
  442.         bytes_written = write(gvw->ghostview.interpreter_input,
  443.                   gvw->ghostview.input_buffer_ptr,
  444.                   gvw->ghostview.buffer_bytes_left);
  445.  
  446.         if (broken_pipe) {
  447.         broken_pipe = False;
  448.         InterpreterFailed(w);        /* Something bad happened */
  449.         } else if (bytes_written == -1) {
  450.         if ((errno != EWOULDBLOCK) && (errno != EAGAIN)) {
  451.             InterpreterFailed(w);    /* Something bad happened */
  452.         }
  453.         } else {
  454.         gvw->ghostview.buffer_bytes_left -= bytes_written;
  455.         gvw->ghostview.input_buffer_ptr += bytes_written;
  456.         }
  457.     }
  458. #ifdef NON_BLOCKING_IO
  459.     } while(gvw->ghostview.ps_input &&
  460.         gvw->ghostview.buffer_bytes_left == 0);
  461. #endif
  462.     signal(SIGPIPE, oldsig);
  463.     if (gvw->ghostview.ps_input == NULL &&
  464.     gvw->ghostview.buffer_bytes_left == 0) {
  465.     if (gvw->ghostview.interpreter_input_id != None) {
  466.         XtRemoveInput(gvw->ghostview.interpreter_input_id);
  467.         gvw->ghostview.interpreter_input_id = None;
  468.     }
  469.     }
  470. }
  471.  
  472. /* Output - receive I/O from ghostscript's stdout and stderr.
  473.  * Pass this to the application via the output_callback. */
  474. static void
  475. Output(client_data, source, id)
  476.     XtPointer client_data;
  477.     int *source;
  478.     XtInputId *id;
  479. {
  480.     Widget w = (Widget) client_data;
  481.     GhostviewWidget gvw = (GhostviewWidget) w;
  482.     char buf[GV_BUFSIZ+1];
  483.     int bytes = 0;
  484.  
  485.     if (*source == gvw->ghostview.interpreter_output) {
  486.     bytes = read(gvw->ghostview.interpreter_output, buf, GV_BUFSIZ);
  487.     if (bytes == 0) { /* EOF occurred */
  488.         close(gvw->ghostview.interpreter_output);
  489.         gvw->ghostview.interpreter_output = -1;
  490.         XtRemoveInput(gvw->ghostview.interpreter_output_id);
  491.         return;
  492.     } else if (bytes == -1) {
  493.         InterpreterFailed(w);        /* Something bad happened */
  494.         return;
  495.     }
  496.     } else if (*source == gvw->ghostview.interpreter_error) {
  497.     bytes = read(gvw->ghostview.interpreter_error, buf, GV_BUFSIZ);
  498.     if (bytes == 0) { /* EOF occurred */
  499.         close(gvw->ghostview.interpreter_error);
  500.         gvw->ghostview.interpreter_error = -1;
  501.         XtRemoveInput(gvw->ghostview.interpreter_error_id);
  502.         return;
  503.     } else if (bytes == -1) {
  504.         InterpreterFailed(w);        /* Something bad happened */
  505.         return;
  506.     }
  507.     }
  508.     if (bytes > 0) {
  509.     buf[bytes] = '\0';
  510.     XtCallCallbackList(w, gvw->ghostview.output_callback, (XtPointer) buf);
  511.     }
  512. }
  513.  
  514. #endif /* VMS */
  515.  
  516. /* Register the type converter required for the PageOrientation. */
  517. /* Register the type converter required for the Palette. */
  518. /* This routine is called exactly once. */
  519. static void
  520. ClassInitialize()
  521. {
  522.     XtSetTypeConverter(XtRString, XtRPageOrientation,
  523.                XmuCvtStringToPageOrientation, NULL, 0,
  524.                XtCacheAll, NULL);
  525.     XtSetTypeConverter(XtRString, XtRPalette,
  526.                XmuCvtStringToPalette, NULL, 0,
  527.                XtCacheAll, NULL);
  528. }
  529.  
  530. /* Get atoms needed to communicate with ghostscript. */
  531. /* This routine is called once per display. */
  532. static void
  533. ClassPartInitialize(class)
  534.     WidgetClass class;
  535. {
  536.     GhostviewWidgetClass gvc = (GhostviewWidgetClass)class;
  537.     gvc->ghostview_class.ghostview = XmuMakeAtom("GHOSTVIEW");
  538.     gvc->ghostview_class.gv_colors = XmuMakeAtom("GHOSTVIEW_COLORS");
  539.     gvc->ghostview_class.next = XmuMakeAtom("NEXT");
  540.     gvc->ghostview_class.page = XmuMakeAtom("PAGE");
  541.     gvc->ghostview_class.done = XmuMakeAtom("DONE");
  542. }
  543.  
  544. /* Initialize private state. */
  545.  
  546. static void
  547. Initialize(request, new, args, num_args)
  548.     Widget request, new;
  549.     ArgList args;        /* unused */
  550.     Cardinal *num_args;    /* unused */
  551. {
  552.     XGCValues    values;
  553.     XtGCMask    mask;
  554.     GhostviewWidget ngvw = (GhostviewWidget) new;
  555.     GhostviewWidget rgvw = (GhostviewWidget) request;
  556.  
  557.     values.foreground = new->core.background_pixel;
  558.     mask = GCForeground;
  559.     ngvw->ghostview.gc = XtGetGC(new, mask, &values);
  560.     ngvw->ghostview.mwin = None;
  561.     ngvw->ghostview.disable_start = False;
  562.     ngvw->ghostview.interpreter_pid = -1;
  563.     ngvw->ghostview.input_buffer = NULL;
  564.     ngvw->ghostview.bytes_left = 0;
  565. #ifndef VMS
  566.     ngvw->ghostview.input_buffer_ptr = NULL;
  567.     ngvw->ghostview.buffer_bytes_left = 0;
  568. #endif
  569.     ngvw->ghostview.ps_input = NULL;
  570.     ngvw->ghostview.interpreter_input = -1;
  571.     ngvw->ghostview.interpreter_output = -1;
  572. #ifndef VMS
  573.     ngvw->ghostview.interpreter_error = -1;
  574.     ngvw->ghostview.interpreter_input_id = None;
  575.     ngvw->ghostview.interpreter_output_id = None;
  576.     ngvw->ghostview.interpreter_error_id = None;
  577. #else /* VMS */
  578.     memset(ngvw->ghostview.interpreter_input_iosb, 0, 8);
  579.     memset(ngvw->ghostview.interpreter_output_iosb, 0, 8);
  580.     ngvw->ghostview.output_buffer = NULL;
  581. #endif /* VMS */
  582.     ngvw->ghostview.gs_width = 0;
  583.     ngvw->ghostview.gs_height = 0;
  584.     ngvw->ghostview.changed = False;
  585.     ngvw->ghostview.busy = False;
  586.  
  587.     /* Compute window size */
  588.     Layout(new, (rgvw->core.width == 0), (rgvw->core.height == 0));
  589. }
  590.  
  591. /* Create Window and start interpreter if needed */
  592. static void
  593. Realize(w, valueMask, attributes)
  594.     Widget w;
  595.     Mask *valueMask;
  596.     XSetWindowAttributes *attributes;
  597. {
  598.     GhostviewWidget gvw = (GhostviewWidget) w;
  599.  
  600.     if (gvw->ghostview.cursor != None) {
  601.     attributes->cursor = gvw->ghostview.cursor;
  602.     *valueMask |= CWCursor;
  603.     }
  604.  
  605.     XtCreateWindow(w, (unsigned int) InputOutput, (Visual *) CopyFromParent,
  606.            *valueMask, attributes);
  607.  
  608.     Setup(w);
  609. }
  610.  
  611. /* Destroy routine: kill the interpreter and release the GC */
  612. static void
  613. Destroy(w)
  614.     Widget w;
  615. {
  616.     GhostviewWidget gvw = (GhostviewWidget) w;
  617.  
  618.     StopInterpreter(w);
  619.     XtReleaseGC(w, gvw->ghostview.gc);
  620.     if (gvw->ghostview.input_buffer) XtFree(gvw->ghostview.input_buffer);
  621. #ifdef VMS
  622.     if (gvw->ghostview.output_buffer) XtFree(gvw->ghostview.output_buffer);
  623. #endif /* VMS */
  624.     if (gvw->core.background_pixmap != XtUnspecifiedPixmap)
  625.     XFreePixmap(XtDisplay(w), gvw->core.background_pixmap);
  626. }
  627.  
  628. /* Process resize request.  Requested size cannot be changed.
  629.  * NOTE: This routine may be called before the widget is realized.
  630.  * (It was a surprise to me.)
  631.  * If the widget is realized, start a new interpreter by calling Setup().
  632.  * If Setup() actually started a new interpreter and it is taking input
  633.  * from stdin, send a refresh message to the application.  This is the
  634.  * only way that the application can be notified that it needs to resend
  635.  * the input because someone forced a new window size on the widget.
  636.  */
  637. static void
  638. Resize(w)
  639.     Widget w;
  640. {
  641.     Layout(w, False, False);
  642.     if (!XtIsRealized(w)) return;
  643.     if (Setup(w)) {
  644.     GhostviewWidget gvw = (GhostviewWidget) w;
  645.     if (gvw->ghostview.filename == NULL) {
  646.         XtCallCallbackList(w, gvw->ghostview.message_callback, "Refresh");
  647.     }
  648.     }
  649. }
  650.  
  651. /* SetValues routine.  Set new private state, based on changed values
  652.  * in the widget.  Always returns False, because redisplay is never needed.
  653.  */
  654. static Boolean
  655. SetValues(current, request, new)
  656.     Widget current, request, new;
  657. {
  658.     GhostviewWidget cgvw = (GhostviewWidget) current;
  659.     GhostviewWidget rgvw = (GhostviewWidget) request;
  660.     GhostviewWidget ngvw = (GhostviewWidget) new;
  661.     String cfilename;
  662.     String rfilename;
  663.     String carguments;
  664.     String rarguments;
  665.  
  666.     cfilename = cgvw->ghostview.filename;
  667.     if (cfilename == NULL) cfilename = "(null)";
  668.     rfilename = rgvw->ghostview.filename;
  669.     if (rfilename == NULL) rfilename = "(null)";
  670.     carguments = cgvw->ghostview.arguments;
  671.     if (carguments == NULL) carguments = "(null)";
  672.     rarguments = rgvw->ghostview.arguments;
  673.     if (rarguments == NULL) rarguments = "(null)";
  674.  
  675.     if (XtIsRealized(new) && !ngvw->ghostview.busy &&
  676.     (cgvw->ghostview.cursor != ngvw->ghostview.cursor)) {
  677.     XDefineCursor(XtDisplay(new), XtWindow(new), ngvw->ghostview.cursor);
  678.     }
  679.     if (XtIsRealized(new) && ngvw->ghostview.busy &&
  680.     (cgvw->ghostview.busy_cursor != ngvw->ghostview.busy_cursor)) {
  681.     XDefineCursor(XtDisplay(new), XtWindow(new),
  682.               ngvw->ghostview.busy_cursor);
  683.     }
  684.     if (cgvw->core.background_pixel != rgvw->core.background_pixel) {
  685.     XGCValues    values;
  686.     XtGCMask    mask;
  687.  
  688.     XtReleaseGC(current, cgvw->ghostview.gc);
  689.     values.foreground = new->core.background_pixel;
  690.     mask = GCForeground;
  691.     ngvw->ghostview.gc = XtGetGC(new, mask, &values);
  692.     }
  693.     if ((cgvw->core.width != rgvw->core.width) ||
  694.     (cgvw->core.height != rgvw->core.height) ||
  695.     (cgvw->core.background_pixel != rgvw->core.background_pixel) ||
  696.     (cgvw->ghostview.foreground != rgvw->ghostview.foreground) ||
  697.     (cgvw->ghostview.palette != rgvw->ghostview.palette) ||
  698.     strcmp(cgvw->ghostview.interpreter, rgvw->ghostview.interpreter) ||
  699.     strcmp(carguments, rarguments) ||
  700.     (cgvw->ghostview.quiet != rgvw->ghostview.quiet) ||
  701.     (cgvw->ghostview.safer != rgvw->ghostview.safer) ||
  702.     strcmp(cfilename, rfilename) ||
  703.     (cgvw->ghostview.orientation != rgvw->ghostview.orientation) ||
  704.     (cgvw->ghostview.use_bpixmap != rgvw->ghostview.use_bpixmap) ||
  705.     (cgvw->ghostview.xdpi != rgvw->ghostview.xdpi) ||
  706.     (cgvw->ghostview.ydpi != rgvw->ghostview.ydpi) ||
  707.     (cgvw->ghostview.bottom_margin != rgvw->ghostview.bottom_margin) ||
  708.     (cgvw->ghostview.left_margin != rgvw->ghostview.left_margin) ||
  709.     (cgvw->ghostview.right_margin != rgvw->ghostview.right_margin) ||
  710.     (cgvw->ghostview.top_margin != rgvw->ghostview.top_margin) ||
  711.     (cgvw->ghostview.llx != rgvw->ghostview.llx) ||
  712.     (cgvw->ghostview.lly != rgvw->ghostview.lly) ||
  713.     (cgvw->ghostview.urx != rgvw->ghostview.urx) ||
  714.     (cgvw->ghostview.ury != rgvw->ghostview.ury)) {
  715.  
  716.     ngvw->ghostview.changed = True;
  717.     Layout(new, True, True);
  718.     }
  719.  
  720.     if (ngvw->ghostview.changed && XtIsRealized(current)) Setup(new);
  721.     return(False);
  722. }
  723.  
  724. /*    Function Name: QueryGeometry
  725.  *    Description: This tells the parent what size we would like to be
  726.  *                   given certain constraints.
  727.  *    Arguments: w - the widget.
  728.  *                 intended - what the parent intends to do with us.
  729.  *                 requested - what we want to happen.
  730.  */
  731.  
  732. static XtGeometryResult 
  733. QueryGeometry(w, intended, requested)
  734. Widget w;
  735. XtWidgetGeometry *intended, *requested;
  736. {
  737.     Dimension new_width, new_height;
  738.     Boolean change, width_req, height_req;
  739.     
  740.     width_req = intended->request_mode & CWWidth;
  741.     height_req = intended->request_mode & CWHeight;
  742.  
  743.     if (width_req)
  744.       new_width = intended->width;
  745.     else
  746.       new_width = w->core.width;
  747.  
  748.     if (height_req)
  749.       new_height = intended->height;
  750.     else
  751.       new_height = w->core.height;
  752.  
  753.     requested->request_mode = 0;
  754.     
  755. /*
  756.  * We only care about our height and width.
  757.  */
  758.  
  759.     if (!width_req && !height_req)
  760.       return(XtGeometryYes);
  761.     
  762.     change = ComputeSize(w, !width_req, !height_req, &new_width, &new_height);
  763.  
  764.     requested->request_mode |= CWWidth;
  765.     requested->width = new_width;
  766.     requested->request_mode |= CWHeight;
  767.     requested->height = new_height;
  768.  
  769.     if (change)
  770.         return(XtGeometryAlmost);
  771.     return(XtGeometryYes);
  772. }
  773.  
  774. /* Layout the widget. */
  775.  
  776. static void
  777. Layout(w, xfree, yfree)
  778.     Widget w;
  779.     Boolean xfree, yfree;
  780. {
  781.     Dimension width = w->core.width;
  782.     Dimension height = w->core.height;
  783.     Boolean different_size = ComputeSize(w, xfree, yfree, &width, &height);
  784.     if (different_size) ChangeSize(w, width, height);
  785. }
  786.  
  787. /* Compute new size of window, sets xdpi and ydpi if necessary.
  788.  * returns True if new window size is different */
  789. static Boolean
  790. ComputeSize(w, xfree, yfree, width, height)
  791.     Widget w;
  792.     Boolean xfree, yfree;    /* Am I allowed to change width or height */
  793.     Dimension *width, *height;
  794. {
  795.     GhostviewWidget gvw = (GhostviewWidget) w;
  796.     Dimension new_width = *width;
  797.     Dimension new_height = *height;
  798.     float newxdpi, newydpi;
  799.     Boolean change;
  800.  
  801.     if (xfree && yfree) {
  802.     /* width and height can be changed, calculate window size according */
  803.     /* to xpdi and ydpi */
  804.     switch (gvw->ghostview.orientation) {
  805.     case XtPageOrientationPortrait:
  806.     case XtPageOrientationUpsideDown:
  807.         new_width = (gvw->ghostview.urx - gvw->ghostview.llx) / 72.0 *
  808.              gvw->ghostview.xdpi + 0.5;
  809.         new_height = (gvw->ghostview.ury - gvw->ghostview.lly) / 72.0 *
  810.               gvw->ghostview.ydpi + 0.5;
  811.         break;
  812.     case XtPageOrientationLandscape:
  813.     case XtPageOrientationSeascape:
  814.         new_width = (gvw->ghostview.ury - gvw->ghostview.lly) / 72.0 *
  815.              gvw->ghostview.xdpi + 0.5;
  816.         new_height = (gvw->ghostview.urx - gvw->ghostview.llx) / 72.0 *
  817.               gvw->ghostview.ydpi + 0.5;
  818.         break;
  819.     }
  820.     } else if (xfree) {
  821.     /* height is fixed.  Preserve aspect ratio by recomputing */
  822.     /* ydpi and xdpi */
  823.     switch (gvw->ghostview.orientation) {
  824.     case XtPageOrientationPortrait:
  825.     case XtPageOrientationUpsideDown:
  826.         newydpi = gvw->core.height * 72.0 /
  827.               (gvw->ghostview.ury - gvw->ghostview.lly);
  828.         newxdpi = newydpi * gvw->ghostview.xdpi / gvw->ghostview.ydpi;
  829.         gvw->ghostview.xdpi = newxdpi;
  830.         gvw->ghostview.ydpi = newydpi;
  831.         new_width = (gvw->ghostview.urx - gvw->ghostview.llx) / 72.0 *
  832.              gvw->ghostview.xdpi + 0.5;
  833.         break;
  834.     case XtPageOrientationLandscape:
  835.     case XtPageOrientationSeascape:
  836.         newydpi = gvw->core.height * 72.0 /
  837.               (gvw->ghostview.urx - gvw->ghostview.llx);
  838.         newxdpi = newydpi * gvw->ghostview.xdpi / gvw->ghostview.ydpi;
  839.         gvw->ghostview.xdpi = newxdpi;
  840.         gvw->ghostview.ydpi = newydpi;
  841.         new_width = (gvw->ghostview.ury - gvw->ghostview.lly) / 72.0 *
  842.              gvw->ghostview.xdpi + 0.5;
  843.         break;
  844.     }
  845.     } else if (yfree) {
  846.     /* width is fixed.  Preserve aspect ratio by recomputing */
  847.     /* xdpi and ydpi */
  848.     switch (gvw->ghostview.orientation) {
  849.     case XtPageOrientationPortrait:
  850.     case XtPageOrientationUpsideDown:
  851.         newxdpi = gvw->core.width * 72.0 /
  852.               (gvw->ghostview.urx - gvw->ghostview.llx);
  853.         newydpi = newxdpi * gvw->ghostview.ydpi / gvw->ghostview.xdpi;
  854.         gvw->ghostview.xdpi = newxdpi;
  855.         gvw->ghostview.ydpi = newydpi;
  856.         new_height = (gvw->ghostview.ury - gvw->ghostview.lly) / 72.0 *
  857.               gvw->ghostview.ydpi + 0.5;
  858.         break;
  859.     case XtPageOrientationLandscape:
  860.     case XtPageOrientationSeascape:
  861.         newxdpi = gvw->core.width * 72.0 /
  862.               (gvw->ghostview.ury - gvw->ghostview.lly);
  863.         newydpi = newxdpi * gvw->ghostview.ydpi / gvw->ghostview.xdpi;
  864.         gvw->ghostview.xdpi = newxdpi;
  865.         gvw->ghostview.ydpi = newydpi;
  866.         new_height = (gvw->ghostview.urx - gvw->ghostview.llx) / 72.0 *
  867.               gvw->ghostview.ydpi + 0.5;
  868.         break;
  869.     }
  870.     } else {
  871.     /* height and width are fixed.  Just have to live with it. */
  872.     switch (gvw->ghostview.orientation) {
  873.     case XtPageOrientationPortrait:
  874.     case XtPageOrientationUpsideDown:
  875.         gvw->ghostview.xdpi = gvw->core.width * 72.0 /
  876.                   (gvw->ghostview.urx - gvw->ghostview.llx);
  877.         gvw->ghostview.ydpi = gvw->core.height * 72.0 /
  878.                   (gvw->ghostview.ury - gvw->ghostview.lly);
  879.         break;
  880.     case XtPageOrientationLandscape:
  881.     case XtPageOrientationSeascape:
  882.         gvw->ghostview.xdpi = gvw->core.width * 72.0 /
  883.                   (gvw->ghostview.ury - gvw->ghostview.lly);
  884.         gvw->ghostview.ydpi = gvw->core.height * 72.0 /
  885.                   (gvw->ghostview.urx - gvw->ghostview.llx);
  886.         break;
  887.     }
  888.     }
  889.  
  890.     change = (new_width != *width) || (new_height != *height);
  891.     *width = new_width;
  892.     *height = new_height;
  893.     return (change);
  894. }
  895.  
  896. /*    Function Name: ChangeSize.
  897.  *    Description: Request a size change.
  898.  *    Arguments: w - the widget to try change the size of.
  899.  */
  900.  
  901. static void
  902. ChangeSize(w, width, height)
  903. Widget w;
  904. Dimension width, height;
  905. {
  906.     XtWidgetGeometry request, reply;
  907.     Boolean changed = False;
  908.  
  909.     request.request_mode = CWWidth | CWHeight;
  910.     request.width = width;
  911.     request.height = height;
  912.     
  913.     switch ( XtMakeGeometryRequest(w, &request, &reply) ) {
  914.     case XtGeometryYes:
  915.     changed = True;
  916.         break;
  917.     case XtGeometryNo:
  918.         break;
  919.     case XtGeometryAlmost:
  920.     ComputeSize(w, (request.height != reply.height),
  921.                (request.width != reply.width),
  922.                &(reply.width), &(reply.height));
  923.     request = reply;
  924.     switch (XtMakeGeometryRequest(w, &request, &reply) ) {
  925.     case XtGeometryYes:
  926.         changed = True;
  927.         break;
  928.     case XtGeometryNo:
  929.         break;
  930.     case XtGeometryAlmost:
  931.         request = reply;
  932.         ComputeSize(w, FALSE, FALSE, &(request.width), &(request.height));
  933.         request.request_mode = CWWidth | CWHeight;
  934.         XtMakeGeometryRequest(w, &request, &reply);
  935.         changed = True;
  936.         break;
  937.     }
  938.     break;
  939.     }
  940.  
  941.     /* If success, setup the widet for the new size. */
  942.     if (changed && XtIsRealized(w)) Setup(w);
  943. }
  944.  
  945. /* Catch the alloc error when there is not enough resources for the
  946.  * backing pixmap.  Automatically shut off backing pixmap and let the
  947.  * user know when this happens.
  948.  */
  949. static Boolean alloc_error;
  950. static XErrorHandler oldhandler;
  951.  
  952. static int
  953. catch_alloc (dpy, err)
  954. Display *dpy;
  955. XErrorEvent *err;
  956. {
  957.     if (err->error_code == BadAlloc) {
  958.     alloc_error = True;
  959.     }
  960.     if (alloc_error) return 0;
  961.     return oldhandler(dpy, err);
  962. }
  963.  
  964. /* Setup - sets up the backing pixmap, and GHOSTVIEW property and
  965.  * starts interpreter if needed.
  966.  * NOTE: the widget must be realized before calling Setup().
  967.  * Returns True if a new interpreter was started, False otherwise.
  968.  */
  969.  
  970. static Boolean
  971. Setup(w)
  972.     Widget w;
  973. {
  974.     GhostviewWidget gvw = (GhostviewWidget) w;
  975.     GhostviewWidgetClass gvc = (GhostviewWidgetClass) XtClass(w);
  976.     char buf[GV_BUFSIZ];
  977.     Pixmap bpixmap;
  978.     XSetWindowAttributes xswa;
  979.  
  980.     if (!gvw->ghostview.changed &&
  981.     (gvw->core.width == gvw->ghostview.gs_width) &&
  982.     (gvw->core.height == gvw->ghostview.gs_height)) return False;
  983.  
  984.     StopInterpreter(w);
  985.  
  986.     if ((gvw->core.width != gvw->ghostview.gs_width) ||
  987.     (gvw->core.height != gvw->ghostview.gs_height) ||
  988.     (!gvw->ghostview.use_bpixmap)) {
  989.     if (gvw->core.background_pixmap != XtUnspecifiedPixmap) {
  990.         XFreePixmap(XtDisplay(w), gvw->core.background_pixmap);
  991.         gvw->core.background_pixmap = XtUnspecifiedPixmap;
  992.         XSetWindowBackgroundPixmap(XtDisplay(w), XtWindow(w), None);
  993.     }
  994.     }
  995.  
  996.     if (gvw->ghostview.use_bpixmap) {
  997.     if (gvw->core.background_pixmap == XtUnspecifiedPixmap) {
  998.         /* Get a Backing Pixmap, but be ready for the BadAlloc. */
  999.         XSync(XtDisplay(w), False);  /* Get to known state */
  1000.         oldhandler = XSetErrorHandler(catch_alloc);
  1001.         alloc_error = False;
  1002.         bpixmap = XCreatePixmap(XtDisplay(w), XtWindow(w),
  1003.                     gvw->core.width, gvw->core.height,
  1004.                     gvw->core.depth);
  1005.         XSync(XtDisplay(w), False);  /* Force the error */
  1006.         if (alloc_error) {
  1007.         XtCallCallbackList(w, gvw->ghostview.message_callback,
  1008.                    "BadAlloc");
  1009.         if (bpixmap != None) {
  1010.             XFreePixmap(XtDisplay(w), bpixmap);
  1011.             XSync(XtDisplay(w), False);  /* Force the error */
  1012.             bpixmap = None;
  1013.         }
  1014.         }
  1015.         oldhandler = XSetErrorHandler(oldhandler);
  1016.         if (bpixmap != None) {
  1017.         gvw->core.background_pixmap = bpixmap;
  1018.         XSetWindowBackgroundPixmap(XtDisplay(w), XtWindow(w),
  1019.                        gvw->core.background_pixmap);
  1020.         }
  1021.     } else {
  1022.         bpixmap = gvw->core.background_pixmap;
  1023.     }
  1024.     } else {
  1025.     if (gvw->core.background_pixmap != XtUnspecifiedPixmap) {
  1026.         XFreePixmap(XtDisplay(w), gvw->core.background_pixmap);
  1027.         gvw->core.background_pixmap = XtUnspecifiedPixmap;
  1028.         XSetWindowBackgroundPixmap(XtDisplay(w), XtWindow(w), None);
  1029.     }
  1030.     bpixmap = None;
  1031.     }
  1032.  
  1033.     if (bpixmap != None) {
  1034.     xswa.backing_store = NotUseful;
  1035.     XChangeWindowAttributes(XtDisplay(w), XtWindow(w),
  1036.                 CWBackingStore, &xswa);
  1037.     } else {
  1038.     xswa.backing_store = Always;
  1039.     XChangeWindowAttributes(XtDisplay(w), XtWindow(w),
  1040.                 CWBackingStore, &xswa);
  1041.     }
  1042.  
  1043.     gvw->ghostview.gs_width = gvw->core.width;
  1044.     gvw->ghostview.gs_height = gvw->core.height;
  1045.  
  1046.     sprintf(buf, "%d %d %d %d %d %d %g %g %d %d %d %d",
  1047.         bpixmap, gvw->ghostview.orientation,
  1048.         gvw->ghostview.llx, gvw->ghostview.lly,
  1049.         gvw->ghostview.urx, gvw->ghostview.ury,
  1050.         gvw->ghostview.xdpi, gvw->ghostview.ydpi,
  1051.         gvw->ghostview.left_margin, gvw->ghostview.bottom_margin,
  1052.         gvw->ghostview.right_margin, gvw->ghostview.top_margin);
  1053.     XChangeProperty(XtDisplay(w), XtWindow(w),
  1054.            XmuInternAtom(XtDisplay(w), gvc->ghostview_class.ghostview),
  1055.            XA_STRING, 8, PropModeReplace,
  1056.            (unsigned char *)buf, strlen(buf));
  1057.  
  1058.     sprintf(buf, "%s %d %d",
  1059.         gvw->ghostview.palette == XtPaletteMonochrome ? "Monochrome" :
  1060.         gvw->ghostview.palette == XtPaletteGrayscale  ? "Grayscale" :
  1061.         gvw->ghostview.palette == XtPaletteColor      ? "Color" : "?",
  1062.         gvw->ghostview.foreground, gvw->core.background_pixel);
  1063.     XChangeProperty(XtDisplay(w), XtWindow(w),
  1064.            XmuInternAtom(XtDisplay(w), gvc->ghostview_class.gv_colors),
  1065.            XA_STRING, 8, PropModeReplace,
  1066.            (unsigned char *)buf, strlen(buf));
  1067.  
  1068.     XSync(XtDisplay(w), False);  /* Be sure to update properties */
  1069.     StartInterpreter(w);
  1070.     return True;
  1071. }
  1072.  
  1073. #ifndef VMS
  1074.  
  1075. /* This routine starts the interpreter.  It sets the DISPLAY and 
  1076.  * GHOSTVIEW environment variables.  The GHOSTVIEW environment variable
  1077.  * contains the Window that ghostscript should write on.
  1078.  *
  1079.  * This routine also opens pipes for stdout and stderr and initializes
  1080.  * application input events for them.  If input to ghostscript is not
  1081.  * from a file, a pipe for stdin is created.  This pipe is setup for
  1082.  * non-blocking I/O so that the user interface never "hangs" because of
  1083.  * a write to ghostscript.
  1084.  */
  1085. static void
  1086. StartInterpreter(w)
  1087.     Widget w;
  1088. {
  1089.     GhostviewWidget gvw = (GhostviewWidget) w;
  1090.     int    std_in[2];
  1091.     int    std_out[2];
  1092.     int    std_err[2];
  1093.     char buf[GV_BUFSIZ];
  1094. #define NUM_ARGS 100
  1095.     char *argv[NUM_ARGS];
  1096.     char *arguments = NULL;
  1097.     char *cptr;
  1098.     int argc = 0;
  1099.     int ret;
  1100.  
  1101.     StopInterpreter(w);
  1102.  
  1103.     /* Clear the window before starting a new interpreter. */
  1104.     if (gvw->core.background_pixmap != XtUnspecifiedPixmap) {
  1105.     XFillRectangle(XtDisplay(w), gvw->core.background_pixmap,
  1106.                gvw->ghostview.gc,
  1107.                0, 0, gvw->core.width, gvw->core.height);
  1108.     }
  1109.     XClearArea(XtDisplay(w), XtWindow(w),
  1110.            0, 0, gvw->core.width, gvw->core.height, False);
  1111.  
  1112.     if (gvw->ghostview.disable_start) return;
  1113.  
  1114.     argv[argc++] = gvw->ghostview.interpreter;
  1115.     argv[argc++] = "-sDEVICE=x11";
  1116.     argv[argc++] = "-dNOPAUSE";
  1117.     if (gvw->ghostview.quiet) argv[argc++] = "-dQUIET";
  1118.     if (gvw->ghostview.safer) argv[argc++] = "-dSAFER";
  1119.     if (gvw->ghostview.arguments) {
  1120.     cptr = arguments = XtNewString(gvw->ghostview.arguments);
  1121.     while (isspace(*cptr)) cptr++;
  1122.     while (*cptr) {
  1123.         argv[argc++] = cptr;
  1124.         while (*cptr && !isspace(*cptr)) cptr++;
  1125.         if (*cptr) *cptr++ = '\0';
  1126.         if (argc + 2 >= NUM_ARGS) {
  1127.         fprintf(stderr, "Too many arguments to interpreter.\n");
  1128.         exit(1);
  1129.         }
  1130.         while (isspace(*cptr)) cptr++;
  1131.     }
  1132.     }
  1133.     argv[argc++] = "-";
  1134.     argv[argc++] = NULL;
  1135.  
  1136.     if (gvw->ghostview.filename == NULL) {
  1137.     ret = pipe(std_in);
  1138.     if (ret == -1) {
  1139.         perror("Could not create pipe");
  1140.         exit(1);
  1141.     }
  1142.     } else if (strcmp(gvw->ghostview.filename, "-")) {
  1143.     std_in[0] = open(gvw->ghostview.filename, O_RDONLY, 0);
  1144.     }
  1145.     ret = pipe(std_out);
  1146.     if (ret == -1) {
  1147.     perror("Could not create pipe");
  1148.     exit(1);
  1149.     }
  1150.     ret = pipe(std_err);
  1151.     if (ret == -1) {
  1152.     perror("Could not create pipe");
  1153.     exit(1);
  1154.     }
  1155.  
  1156.     gvw->ghostview.changed = False;
  1157.     gvw->ghostview.busy = True;
  1158.     XDefineCursor(XtDisplay(gvw), XtWindow(gvw), gvw->ghostview.busy_cursor);
  1159. #if defined(SYSV) || defined(USG)
  1160. #define vfork fork
  1161. #endif
  1162.     gvw->ghostview.interpreter_pid = vfork();
  1163.  
  1164.     if (gvw->ghostview.interpreter_pid == 0) { /* child */
  1165.     close(std_out[0]);
  1166.     close(std_err[0]);
  1167.     dup2(std_out[1], 1);
  1168.     close(std_out[1]);
  1169.     dup2(std_err[1], 2);
  1170.     close(std_err[1]);
  1171.     sprintf(buf, "%d", XtWindow(w));
  1172.     setenv("GHOSTVIEW", buf, True);
  1173.     setenv("DISPLAY", XDisplayString(XtDisplay(w)), True);
  1174.     if (gvw->ghostview.filename == NULL) {
  1175.         close(std_in[1]);
  1176.         dup2(std_in[0], 0);
  1177.         close(std_in[0]);
  1178.     } else if (strcmp(gvw->ghostview.filename, "-")) {
  1179.         dup2(std_in[0], 0);
  1180.         close(std_in[0]);
  1181.     }
  1182.     execvp(argv[0], argv);
  1183.     sprintf(buf, "Exec of %s failed", argv[0]);
  1184.     perror(buf);
  1185.     _exit(1);
  1186.     } else {
  1187.     if (gvw->ghostview.filename == NULL) {
  1188. #ifdef NON_BLOCKING_IO
  1189.         int result;
  1190. #endif
  1191.         close(std_in[0]);
  1192.  
  1193. #ifdef NON_BLOCKING_IO
  1194.         result = fcntl(std_in[1], F_GETFL, 0);
  1195.         result = result | O_NONBLOCK;
  1196.         result = fcntl(std_in[1], F_SETFL, result);
  1197. #endif
  1198.         gvw->ghostview.interpreter_input = std_in[1];
  1199.         gvw->ghostview.interpreter_input_id = None;
  1200.     } else if (strcmp(gvw->ghostview.filename, "-")) {
  1201.         close(std_in[0]);
  1202.     }
  1203.     close(std_out[1]);
  1204.     gvw->ghostview.interpreter_output = std_out[0];
  1205.     gvw->ghostview.interpreter_output_id = 
  1206.         XtAppAddInput(XtWidgetToApplicationContext(w), std_out[0],
  1207.               (XtPointer)XtInputReadMask, Output, (XtPointer)w);
  1208.     close(std_err[1]);
  1209.     gvw->ghostview.interpreter_error = std_err[0];
  1210.     gvw->ghostview.interpreter_error_id = 
  1211.         XtAppAddInput(XtWidgetToApplicationContext(w), std_err[0],
  1212.               (XtPointer)XtInputReadMask, Output, (XtPointer)w);
  1213.     }
  1214.     if (arguments) XtFree(arguments);
  1215. }
  1216.  
  1217. /* Stop the interperter, if present, and remove any Input sources. */
  1218. /* Also reset the busy state. */
  1219. static void
  1220. StopInterpreter(w)
  1221.     Widget w;
  1222. {
  1223.     GhostviewWidget gvw = (GhostviewWidget) w;
  1224.     if (gvw->ghostview.interpreter_pid >= 0) {
  1225.     kill(gvw->ghostview.interpreter_pid, SIGTERM);
  1226.     wait(0);
  1227.     gvw->ghostview.interpreter_pid = -1;
  1228.     }
  1229.     if (gvw->ghostview.interpreter_input >= 0) {
  1230.     close(gvw->ghostview.interpreter_input);
  1231.     gvw->ghostview.interpreter_input = -1;
  1232.     if (gvw->ghostview.interpreter_input_id != None) {
  1233.         XtRemoveInput(gvw->ghostview.interpreter_input_id);
  1234.         gvw->ghostview.interpreter_input_id = None;
  1235.     }
  1236.     while (gvw->ghostview.ps_input) {
  1237.         struct record_list *ps_old = gvw->ghostview.ps_input;
  1238.         gvw->ghostview.ps_input = ps_old->next;
  1239.         if (ps_old->close) fclose(ps_old->fp);
  1240.         XtFree((char *)ps_old);
  1241.     }
  1242.     }
  1243.     if (gvw->ghostview.interpreter_output >= 0) {
  1244.     close(gvw->ghostview.interpreter_output);
  1245.     gvw->ghostview.interpreter_output = -1;
  1246.     XtRemoveInput(gvw->ghostview.interpreter_output_id);
  1247.     }
  1248.     if (gvw->ghostview.interpreter_error >= 0) {
  1249.     close(gvw->ghostview.interpreter_error);
  1250.     gvw->ghostview.interpreter_error = -1;
  1251.     XtRemoveInput(gvw->ghostview.interpreter_error_id);
  1252.     }
  1253.     gvw->ghostview.busy = False;
  1254.     XDefineCursor(XtDisplay(gvw), XtWindow(gvw), gvw->ghostview.cursor);
  1255. }
  1256.  
  1257. #endif /* VMS */
  1258.  
  1259. /* The interpeter failed, Stop what's left and notify application */
  1260. static void
  1261. InterpreterFailed(w)
  1262.     Widget w;
  1263. {
  1264.     GhostviewWidget gvw = (GhostviewWidget) w;
  1265.     StopInterpreter(w);
  1266.     XtCallCallbackList(w, gvw->ghostview.message_callback, "Failed");
  1267. }
  1268.  
  1269. /*
  1270.  *    Public Routines
  1271.  */
  1272.  
  1273. /* GhostviewDisableInterpreter:
  1274.  * Stop any interpreter and disable new ones from starting.
  1275.  */
  1276. void
  1277. GhostviewDisableInterpreter(w)
  1278.     Widget w;
  1279. {
  1280.     GhostviewWidget gvw = (GhostviewWidget) w;
  1281.     gvw->ghostview.disable_start = True;
  1282.     if (XtIsRealized(w)) StopInterpreter(w);
  1283. }
  1284.  
  1285. /* GhostviewDisableInterpreter:
  1286.  * Allow an interpreter to start and start one if the widget is
  1287.  * currently realized.
  1288.  */
  1289. void
  1290. GhostviewEnableInterpreter(w)
  1291.     Widget w;
  1292. {
  1293.     GhostviewWidget gvw = (GhostviewWidget) w;
  1294.     gvw->ghostview.disable_start = False;
  1295.     if (XtIsRealized(w)) StartInterpreter(w);
  1296. }
  1297.  
  1298. /* GhostviewIsInterpreterReady:
  1299.  * Returns True if the interpreter is ready for new input.
  1300.  */
  1301. Boolean
  1302. GhostviewIsInterpreterReady(w)
  1303.     Widget w;
  1304. {
  1305.     GhostviewWidget gvw = (GhostviewWidget) w;
  1306.     return gvw->ghostview.interpreter_pid != -1 &&
  1307.        !gvw->ghostview.busy &&
  1308.        gvw->ghostview.ps_input == NULL;
  1309. }
  1310.  
  1311. /* GhostviewIsInterpreterRunning:
  1312.  * Returns True if the interpreter is running.
  1313.  */
  1314. Boolean
  1315. GhostviewIsInterpreterRunning(w)
  1316.     Widget w;
  1317. {
  1318.     GhostviewWidget gvw = (GhostviewWidget) w;
  1319.     return gvw->ghostview.interpreter_pid != -1;
  1320. }
  1321.  
  1322. /* GhostviewGetBackingPixmap:
  1323.  * Returns the current backing pixmap.
  1324.  */
  1325. Pixmap
  1326. GhostviewGetBackingPixmap(w)
  1327.     Widget w;
  1328. {
  1329.     GhostviewWidget gvw = (GhostviewWidget) w;
  1330.     if (gvw->core.background_pixmap != XtUnspecifiedPixmap)
  1331.     return(gvw->core.background_pixmap);
  1332.     else
  1333.     return(None);
  1334. }
  1335.  
  1336. #ifndef VMS
  1337.  
  1338. /* GhostviewSendPS:
  1339.  *   Queue a portion of a PostScript file for output to ghostscript.
  1340.  *   fp: FILE * of the file in question.  NOTE: if you have several
  1341.  *   Ghostview widgets reading from the same file.  You must open
  1342.  *   a unique FILE * for each widget.
  1343.  *   SendPS does not actually send the PostScript, it merely queues it
  1344.  *   for output.
  1345.  *   begin: position in file (returned from ftell()) to start.
  1346.  *   len:   number of bytes to write.
  1347.  *
  1348.  *   If an interpreter is not running, nothing is queued and
  1349.  *   False is returned.
  1350.  */
  1351. Boolean
  1352. GhostviewSendPS(w, fp, begin, len, close)
  1353.     Widget w;
  1354.     FILE *fp;
  1355.     long begin;
  1356.     unsigned int len;
  1357.     Bool close;
  1358. {
  1359.     GhostviewWidget gvw = (GhostviewWidget) w;
  1360.     struct record_list *ps_new;
  1361.  
  1362.     if (gvw->ghostview.interpreter_input < 0) return False;
  1363.     ps_new = (struct record_list *) XtMalloc(sizeof (struct record_list));
  1364.     ps_new->fp = fp;
  1365.     ps_new->begin = begin;
  1366.     ps_new->len = len;
  1367.     ps_new->seek_needed = True;
  1368.     ps_new->close = close;
  1369.     ps_new->next = NULL;
  1370.  
  1371.     if (gvw->ghostview.input_buffer == NULL) {
  1372.     gvw->ghostview.input_buffer = XtMalloc(GV_BUFSIZ);
  1373.     }
  1374.  
  1375.     if (gvw->ghostview.ps_input == NULL) {
  1376.     gvw->ghostview.input_buffer_ptr = gvw->ghostview.input_buffer;
  1377.     gvw->ghostview.bytes_left = len;
  1378.     gvw->ghostview.buffer_bytes_left = 0;
  1379.     gvw->ghostview.ps_input = ps_new;
  1380.     gvw->ghostview.interpreter_input_id =
  1381.         XtAppAddInput(XtWidgetToApplicationContext(w),
  1382.                   gvw->ghostview.interpreter_input,
  1383.                   (XtPointer)XtInputWriteMask, Input, (XtPointer)w);
  1384.     } else {
  1385.     struct record_list *p = gvw->ghostview.ps_input;
  1386.     while (p->next != NULL) {
  1387.         p = p->next;
  1388.     }
  1389.     p->next = ps_new;
  1390.     }
  1391.     return True;
  1392. }
  1393.  
  1394. #endif /* VMS */
  1395.  
  1396. /* GhostviewNextPage:
  1397.  *   Tell ghostscript to start the next page.
  1398.  *   Returns False if ghostscript is not running, or not ready to start
  1399.  *   another page.
  1400.  *   If another page is started.  Sets the busy flag and cursor.
  1401.  */
  1402. Boolean
  1403. GhostviewNextPage(w)
  1404.     Widget w;
  1405. {
  1406.     GhostviewWidget gvw = (GhostviewWidget) w;
  1407.     GhostviewWidgetClass gvc = (GhostviewWidgetClass) XtClass(w);
  1408.     XEvent event;
  1409.  
  1410.     if (gvw->ghostview.interpreter_pid < 0) return False;
  1411.     if (gvw->ghostview.mwin == None) return False;
  1412.  
  1413.     if (!gvw->ghostview.busy) {
  1414.     gvw->ghostview.busy = True;
  1415.     XDefineCursor(XtDisplay(gvw), XtWindow(gvw),
  1416.               gvw->ghostview.busy_cursor);
  1417.  
  1418.     event.xclient.type = ClientMessage;
  1419.     event.xclient.display = XtDisplay(w);
  1420.     event.xclient.window = gvw->ghostview.mwin;
  1421.     event.xclient.message_type =
  1422.         XmuInternAtom(XtDisplay(w), gvc->ghostview_class.next);
  1423.     event.xclient.format = 32;
  1424.     XSendEvent(XtDisplay(w), gvw->ghostview.mwin, False, 0, &event);
  1425.     XFlush(XtDisplay(w));    /* And push it out */
  1426.     return True;
  1427.     } else {
  1428.     return False;
  1429.     }
  1430. }
  1431.  
  1432. #define    done(type, value) \
  1433.     {                            \
  1434.         if (toVal->addr != NULL) {                \
  1435.         if (toVal->size < sizeof(type)) {        \
  1436.             toVal->size = sizeof(type);            \
  1437.             return False;                \
  1438.         }                        \
  1439.         *(type*)(toVal->addr) = (value);        \
  1440.         }                            \
  1441.         else {                        \
  1442.         static type static_val;                \
  1443.         static_val = (value);                \
  1444.         toVal->addr = (XPointer)&static_val;        \
  1445.         }                            \
  1446.         toVal->size = sizeof(type);                \
  1447.         return True;                    \
  1448.     }
  1449.  
  1450. /* PageOrienation Conversion Routine.
  1451.  * Returns True if Conversion is successful.
  1452.  */
  1453. Boolean
  1454. XmuCvtStringToPageOrientation(dpy, args, num_args, fromVal, toVal, data)
  1455.     Display    *dpy;
  1456.     XrmValue    *args;        /* unused */
  1457.     Cardinal    *num_args;    /* unused */
  1458.     XrmValue    *fromVal;
  1459.     XrmValue    *toVal;
  1460.     XtPointer    *data;        /* unused */
  1461. {
  1462.     static XrmQuark        XrmQEportrait;
  1463.     static XrmQuark        XrmQElandscape;
  1464.     static XrmQuark        XrmQEupsideDown;
  1465.     static XrmQuark        XrmQEseascape;
  1466.     static int            haveQuarks;
  1467.     XrmQuark    q;
  1468.     char    *str = (XPointer) fromVal->addr;
  1469.     char        lowerName[1000];
  1470.  
  1471.     if (str == NULL) return False;
  1472.  
  1473.     if (!haveQuarks) {
  1474.     XrmQEportrait   = XrmStringToQuark(XtEportrait);
  1475.     XrmQElandscape  = XrmStringToQuark(XtElandscape);
  1476.     XrmQEupsideDown = XrmStringToQuark(XtEupsideDown);
  1477.     XrmQEseascape   = XrmStringToQuark(XtEseascape);
  1478.     haveQuarks = 1;
  1479.     }
  1480.  
  1481.     XmuCopyISOLatin1Lowered(lowerName, str);
  1482.  
  1483.     q = XrmStringToQuark(lowerName);
  1484.  
  1485.     if (q == XrmQEportrait)
  1486.     done(XtPageOrientation, XtPageOrientationPortrait);
  1487.     if (q == XrmQElandscape)
  1488.     done(XtPageOrientation, XtPageOrientationLandscape);
  1489.     if (q == XrmQEupsideDown)
  1490.     done(XtPageOrientation, XtPageOrientationUpsideDown);
  1491.     if (q == XrmQEseascape)
  1492.     done(XtPageOrientation, XtPageOrientationSeascape);
  1493.  
  1494.     XtDisplayStringConversionWarning(dpy, str, XtRPageOrientation);
  1495.     return False;
  1496. }
  1497.  
  1498. /* Palette Conversion Routine.
  1499.  * Returns True if Conversion is successful.
  1500.  */
  1501. Boolean
  1502. XmuCvtStringToPalette(dpy, args, num_args, fromVal, toVal, data)
  1503.     Display    *dpy;
  1504.     XrmValue    *args;        /* unused */
  1505.     Cardinal    *num_args;    /* unused */
  1506.     XrmValue    *fromVal;
  1507.     XrmValue    *toVal;
  1508.     XtPointer    *data;        /* unused */
  1509. {
  1510.     static XrmQuark        XrmQEmonochrome;
  1511.     static XrmQuark        XrmQEgrayscale;
  1512.     static XrmQuark        XrmQEcolor;
  1513.     static int            haveQuarks;
  1514.     XrmQuark    q;
  1515.     char    *str = (XPointer) fromVal->addr;
  1516.     char        lowerName[1000];
  1517.  
  1518.     if (str == NULL) return False;
  1519.  
  1520.     if (!haveQuarks) {
  1521.     XrmQEmonochrome = XrmStringToQuark(XtEmonochrome);
  1522.     XrmQEgrayscale  = XrmStringToQuark(XtEgrayscale);
  1523.     XrmQEcolor      = XrmStringToQuark(XtEcolor);
  1524.     haveQuarks = 1;
  1525.     }
  1526.  
  1527.     XmuCopyISOLatin1Lowered(lowerName, str);
  1528.  
  1529.     q = XrmStringToQuark(lowerName);
  1530.  
  1531.     if (q == XrmQEmonochrome)
  1532.     done(XtPalette, XtPaletteMonochrome);
  1533.     if (q == XrmQEgrayscale)
  1534.     done(XtPalette, XtPaletteGrayscale);
  1535.     if (q == XrmQEcolor)
  1536.     done(XtPalette, XtPaletteColor);
  1537.  
  1538.     XtDisplayStringConversionWarning(dpy, str, XtRPalette);
  1539.     return False;
  1540. }
  1541.  
  1542. #ifdef VMS
  1543.  
  1544. /*
  1545. ** VMS specific include files
  1546. */
  1547. #include <descrip.h>
  1548. #include <ssdef.h>
  1549. #include <clidef.h>
  1550. #include <lnmdef.h>
  1551. #include <iodef.h>
  1552. #include <dvidef.h>
  1553. #include "vms_types.h"
  1554.  
  1555. #define ERR_SIGNAL(s) if(!((s) & 1))lib$signal((s), 0, 0)
  1556. #define XtEFN 23
  1557.  
  1558. struct g_l_i
  1559. {
  1560.     GhostviewWidget w;
  1561.     struct g_l_i *next;
  1562. };
  1563.  
  1564. typedef struct g_l_i GhostListItem, *GLI_p;
  1565.  
  1566. static GhostListItem glhead = {(GhostviewWidget) -1, NULL};
  1567. static GLI_p GL = &glhead;
  1568. static size_t GLI_Size = sizeof(GhostListItem);
  1569. static XtInputId EventId;
  1570.  
  1571. /*
  1572. ** This routine is passed to XtAppAddInput(). It is called whenever the event
  1573. ** flag number XtEFN is set and the Xt main loop becomes idle. It clears the
  1574. ** event flag and then scans all the ghostview widgets for completed I/O
  1575. ** requests, processing each as they are found. We have to do them all because
  1576. ** there is no way to have Xt dispatch them individually without a window of
  1577. ** vulnerability that can cause missed events, or by using a separate event
  1578. ** flag for each I/O stream. Event flags are, unfortunately, a limited
  1579. ** resource.
  1580. */
  1581. static Boolean
  1582. IOProcess()
  1583. {
  1584.     GhostviewWidget gvw;
  1585.     GLI_p cur;
  1586.  
  1587.     /*
  1588.     ** Before we process any I/O's, clear the event flag.
  1589.     */
  1590.     sys$clref(XtEFN);
  1591.     /*
  1592.     ** Scan all the ghostview widgets and check for completed I/O's
  1593.     */
  1594.     for(cur = GL->next; cur; cur = cur->next){
  1595.     /*
  1596.     ** Get the widget and check for I/O complete on either mailbox.
  1597.     */
  1598.     gvw = cur->w;
  1599.     if(gvw->ghostview.interpreter_input_iosb[0])Input(gvw);
  1600.     if(gvw->ghostview.interpreter_output_iosb[0])Output(gvw);
  1601.     }
  1602. }
  1603.     
  1604. /*
  1605. ** This is an AST routine. It is called asynchronously whenever one of our
  1606. ** mailbox I/O's completes.
  1607. */
  1608. static void
  1609. IOComplete(client_data)
  1610.     XtPointer client_data;
  1611. {
  1612.     /*
  1613.     ** Set the event flag to tell Xt to call IOProcess.
  1614.     */
  1615.     sys$setef(XtEFN);
  1616. }
  1617.  
  1618. static void
  1619. GLInsert(w)
  1620.     GhostviewWidget w;
  1621. {
  1622.     GLI_p new;
  1623.     int first;
  1624.     
  1625.     /*
  1626.     ** Insert this widget after the list head
  1627.     */
  1628.     first = (GL->next == NULL);
  1629.     new = XtMalloc(GLI_Size);
  1630.     new->w = w;
  1631.     new->next = GL->next;
  1632.     GL->next = new;
  1633.     /*
  1634.     ** If this is the first item on the list, call XtAppAddInput()
  1635.     */
  1636.     if(first)EventId = XtAppAddInput(XtWidgetToApplicationContext(w), XtEFN, 0, 
  1637.     IOProcess, 0);
  1638. }
  1639.  
  1640. static void
  1641. GLRemove(w)
  1642.     GhostviewWidget w;
  1643. {
  1644.     GLI_p prev, cur;
  1645.     int last = 0;
  1646.  
  1647.     /*
  1648.     ** Find and remove this widget from the list.
  1649.     */
  1650.     prev = GL;
  1651.     cur = prev->next;
  1652.     while(cur && cur->w != w){
  1653.     prev = cur;
  1654.     cur = cur->next;
  1655.     }
  1656.     if(cur){
  1657.        prev->next = cur->next;
  1658.        XtFree(cur);
  1659.        last = (GL->next == NULL);
  1660.     }
  1661.     /*
  1662.     ** If this was the last item on the list, call XtRemoveInput()
  1663.     */
  1664.     if(last)XtRemoveInput(EventId);
  1665. }
  1666.  
  1667. /* Input - Feed data to ghostscript's stdin.
  1668.  * Write bytes to ghostscript using non-blocking I/O.
  1669.  * Also, pipe signals are caught during writing.  The return
  1670.  * values are checked and the appropriate action is taken.  I do
  1671.  * this at this low level, because it may not be appropriate for
  1672.  * SIGPIPE to be caught for the overall application.
  1673.  */
  1674.  
  1675. static void
  1676. Input(gvw)
  1677.     GhostviewWidget gvw;
  1678. {
  1679.     int stat, bbytes;
  1680.     char *ch;
  1681.  
  1682.     /*
  1683.     ** Check for error on previous I/O.
  1684.     */
  1685.     stat = gvw->ghostview.interpreter_input_iosb[0];
  1686.     if(stat != SS$_NORMAL){
  1687.     InterpreterFailed(gvw);
  1688.     } else {
  1689.  
  1690.     /* Get a new section if required */
  1691.     
  1692.     if (gvw->ghostview.ps_input && gvw->ghostview.bytes_left == 0) {
  1693.         struct record_list *ps_old = gvw->ghostview.ps_input;
  1694.         gvw->ghostview.ps_input = ps_old->next;
  1695.         if (ps_old->close) fclose(ps_old->fp);
  1696.         XtFree((char *)ps_old);
  1697.     }
  1698.     if(gvw->ghostview.ps_input){
  1699.         /* Have to seek at the beginning of each section */
  1700.         if (gvw->ghostview.ps_input->seek_needed) {
  1701.         if (gvw->ghostview.ps_input->len > 0)
  1702.             fseek(gvw->ghostview.ps_input->fp,
  1703.               gvw->ghostview.ps_input->begin, SEEK_SET);
  1704.         gvw->ghostview.ps_input->seek_needed = False;
  1705.         gvw->ghostview.bytes_left = gvw->ghostview.ps_input->len;
  1706.         }
  1707.         /*
  1708.         ** Read a line from the file.
  1709.         */
  1710.         ch = fgets(gvw->ghostview.input_buffer, GV_BUFSIZ,
  1711.         gvw->ghostview.ps_input->fp);
  1712.         if(!ch){
  1713.         /*
  1714.         ** Error, EOF when there's supposed to be data left. 
  1715.         */
  1716.         InterpreterFailed(gvw);
  1717.         } else {
  1718.         /*
  1719.         ** Write it to the mailbox.
  1720.         */
  1721.         bbytes = strlen(gvw->ghostview.input_buffer);
  1722.         gvw->ghostview.bytes_left -= bbytes;
  1723.         stat = sys$qio(0, (short)gvw->ghostview.interpreter_input,
  1724.             IO$_WRITEVBLK, &gvw->ghostview.interpreter_input_iosb,
  1725.             IOComplete, 0, gvw->ghostview.input_buffer, bbytes,
  1726.             0, 0, 0, 0);
  1727.         ERR_SIGNAL(stat);
  1728.         }
  1729.     }
  1730.     }
  1731. }
  1732.  
  1733. /* Output - receive I/O from ghostscript's stdout and stderr.
  1734.  * Pass this to the application via the output_callback. */
  1735. static void
  1736. Output(gvw)
  1737.     GhostviewWidget gvw;
  1738. {
  1739.     char buf[GV_BUFSIZ+1];
  1740.     int bytes, stat;
  1741.  
  1742.     stat = gvw->ghostview.interpreter_output_iosb[0];
  1743.     bytes = gvw->ghostview.interpreter_output_iosb[1];
  1744.     if (stat == SS$_NORMAL) {
  1745.     /*
  1746.     ** Got a message. If line complete, pass to the output_callback.
  1747.     **
  1748.     ** HACK ALERT, if bytes is -1 nothing happens, but an I/O is queued.
  1749.     ** This is our first time code, since Xt doesn't queue the I/O for us
  1750.     ** under VMS, just watches for completion. In StartInterpreter We setup
  1751.     ** an IOSB with a success status and -1 bytes so Xt will call us the
  1752.     ** first time to get the I/O queued.
  1753.     */
  1754.     if (bytes == 0) {
  1755.         strcpy(buf, "\n");
  1756.     } else if (bytes == 1) {
  1757.         buf[0] = gvw->ghostview.output_buffer[0];
  1758.         buf[1] = '\0';
  1759.     } else if (bytes > 1) {
  1760.         /*
  1761.         ** Copy the message to a local buffer and pass it to the callback.
  1762.         */
  1763.         memcpy(buf, gvw->ghostview.output_buffer, bytes);
  1764.         buf[bytes] = '\0';
  1765.     }
  1766.     if(bytes >= 0)XtCallCallbackList(gvw, gvw->ghostview.output_callback,
  1767.         (XtPointer) buf);
  1768.     /*
  1769.     ** Queue a new read to the mailbox
  1770.     */
  1771.     stat = sys$qio(0, (short)gvw->ghostview.interpreter_output,
  1772.         IO$_READVBLK, &gvw->ghostview.interpreter_output_iosb, IOComplete,
  1773.         0, gvw->ghostview.output_buffer, GV_BUFSIZ, 0, 0, 0, 0);
  1774.     ERR_SIGNAL(stat);
  1775.     } else {
  1776.     InterpreterFailed(gvw);        /* Something bad happened */
  1777.     }
  1778. }
  1779.  
  1780. /* This routine starts the interpreter.  It sets the DISPLAY and 
  1781.  * GHOSTVIEW environment variables.  The GHOSTVIEW environment variable
  1782.  * contains the Window that ghostscript should write on.
  1783.  *
  1784.  * This routine also opens pipes for stdout and stderr and initializes
  1785.  * application input events for them.  If input to ghostscript is not
  1786.  * from a file, a pipe for stdin is created.  This pipe is setup for
  1787.  * non-blocking I/O so that the user interface never "hangs" because of
  1788.  * a write to ghostscript.
  1789.  */
  1790. static void
  1791. StartInterpreter(w)
  1792.     Widget w;
  1793. {
  1794.     GhostviewWidget gvw = (GhostviewWidget) w;
  1795.     char buf[GV_BUFSIZ];
  1796.     char cmd[512];
  1797.     int ret;
  1798.     short ch1, ch2;
  1799.     char in_mbx_name[65], out_mbx_name[65];
  1800.     long pid, nowait = CLI$M_NOWAIT;
  1801.     const $DESCRIPTOR(ghostview_desc, "GHOSTVIEW");
  1802.     const $DESCRIPTOR(display_desc, "DECW$DISPLAY");
  1803.     const $DESCRIPTOR(lnt_desc, "LNM$PROCESS");
  1804.     $DESCRIPTOR(in_desc, "");
  1805.     $DESCRIPTOR(out_desc, "");
  1806.     $DESCRIPTOR(lnm_desc, "");
  1807.     $DESCRIPTOR(cmd_desc, cmd);
  1808.     ITEM_LIST_3_T(gv_list, 1) = {{{0, LNM$_STRING, buf, NULL}}, 0};
  1809.     ITEM_LIST_3_T(dis_list, 1) = {{{0, LNM$_STRING, NULL, NULL}}, 0};
  1810.     ITEM_LIST_3_T(dvi_list, 1) = {{{64, DVI$_DEVNAM, NULL, NULL}}, 0};
  1811.     IOSB_GET_T dvi_iosb;
  1812.  
  1813.     /*
  1814.     ** Stop interpreter if running
  1815.     */
  1816.     StopInterpreter(w);
  1817.     /*
  1818.     ** Clear the window before starting a new interpreter.
  1819.     */
  1820.     if (gvw->core.background_pixmap != XtUnspecifiedPixmap) {
  1821.     XFillRectangle(XtDisplay(w), gvw->core.background_pixmap,
  1822.                gvw->ghostview.gc,
  1823.                0, 0, gvw->core.width, gvw->core.height);
  1824.     }
  1825.     XClearArea(XtDisplay(w), XtWindow(w),
  1826.            0, 0, gvw->core.width, gvw->core.height, False);
  1827.     /*
  1828.     ** Check for disabled.
  1829.     */
  1830.     if (gvw->ghostview.disable_start) return;
  1831.     /*
  1832.     ** Build Ghostscript startup command
  1833.     */
  1834.     strcpy(cmd, gvw->ghostview.interpreter);
  1835.     strcat(cmd, " \"-sDEVICE=x11\" \"-dNOPAUSE\" ");
  1836.     if (gvw->ghostview.quiet) strcat(cmd, "\"-dQUIET\" ");
  1837.     if (gvw->ghostview.safer) strcat(cmd, "\"-dSAFER\" ");
  1838.     if (gvw->ghostview.arguments) {
  1839.     strcat(cmd, gvw->ghostview.arguments);
  1840.     strcat(cmd, " ");
  1841.     }
  1842.     strcat(cmd, "\"-\" ");
  1843.  
  1844.     /*
  1845.     ** Determine input source.
  1846.     */
  1847.     if (gvw->ghostview.filename == NULL) {
  1848.     /*
  1849.     ** Create a mailbox to feed input to Ghostscript and get its name.
  1850.     */
  1851.     ret = sys$crembx(0, &ch1, GV_BUFSIZ, GV_BUFSIZ, 0, 0, 0, 0);
  1852.     ERR_SIGNAL(ret);
  1853.     dvi_list.item[0].buffer_p = in_mbx_name;
  1854.     ret = sys$getdvi(0, ch1, 0, &dvi_list, &dvi_iosb, 0, 0, 0);
  1855.     ERR_SIGNAL(ret); ERR_SIGNAL(dvi_iosb.status);
  1856.     in_mbx_name[64] = '\0';
  1857.     in_desc.dsc$a_pointer = in_mbx_name;
  1858.     in_desc.dsc$w_length = strlen(in_mbx_name);
  1859.     } else {
  1860.     /*
  1861.     ** Set up file name to give Ghostscript as standard input.
  1862.     */
  1863.     in_desc.dsc$a_pointer = gvw->ghostview.filename;
  1864.     in_desc.dsc$w_length = strlen(gvw->ghostview.filename);
  1865.     }
  1866.     /*
  1867.     ** Create mailbox to receive Ghostscript's output
  1868.     */
  1869.     ret = sys$crembx(0, &ch2, GV_BUFSIZ, GV_BUFSIZ, 0, 0, 0, 0);
  1870.     ERR_SIGNAL(ret);
  1871.     dvi_list.item[0].buffer_p = out_mbx_name;
  1872.     ret = sys$getdvi(0, ch2, 0, &dvi_list, &dvi_iosb, 0, 0, 0);
  1873.     ERR_SIGNAL(ret); ERR_SIGNAL(dvi_iosb.status);
  1874.     out_mbx_name[64] = '\0';
  1875.     out_desc.dsc$a_pointer = out_mbx_name;
  1876.     out_desc.dsc$w_length = strlen(out_mbx_name);
  1877.     /*
  1878.     ** Create GHOSTVIEW and DECW$DISPLAY logical names.
  1879.     **
  1880.     ** We use CRELNM rather than LIB$SET_LOGICAL because we want these to be
  1881.     ** user mode and go away when the program exits. It doesn't matter that we
  1882.     ** may set them multiple times, as with the mailbox logicals, since once
  1883.     ** Ghostscript starts we don't need them any more.
  1884.     */
  1885.     sprintf(buf, "%d", XtWindow(w));
  1886.     gv_list.item[0].buffer_size = strlen(buf);
  1887.     ret = sys$crelnm(0, &lnt_desc, &ghostview_desc, 0, &gv_list);
  1888.     ERR_SIGNAL(ret);
  1889.     dis_list.item[0].buffer_p = XDisplayString(XtDisplay(w));
  1890.     dis_list.item[0].buffer_size = strlen(dis_list.item[0].buffer_p);
  1891.     ret = sys$crelnm(0, &lnt_desc, &display_desc, 0, &dis_list);
  1892.     ERR_SIGNAL(ret);
  1893.     /*
  1894.     ** Spawn Ghostscript process
  1895.     */
  1896.     gvw->ghostview.changed = False;
  1897.     gvw->ghostview.busy = True;
  1898.     cmd_desc.dsc$w_length = strlen(cmd);
  1899.     ret = lib$spawn(&cmd_desc, &in_desc, &out_desc, &nowait, 0, &pid, 0, 0,
  1900.     0, 0, 0, 0, 0);
  1901.     ERR_SIGNAL(ret);
  1902.     XDefineCursor(XtDisplay(gvw), XtWindow(gvw), gvw->ghostview.busy_cursor);
  1903.     /*
  1904.     ** Everything worked, initialize IOSBs and save info about interpretter.
  1905.     */
  1906.     gvw->ghostview.interpreter_pid = pid;
  1907.     if (gvw->ghostview.filename == NULL) {
  1908.     gvw->ghostview.interpreter_input = ch1;
  1909.     gvw->ghostview.interpreter_input_iosb[0] = 0;
  1910.     }
  1911.     gvw->ghostview.interpreter_output = ch2;
  1912.     if (gvw->ghostview.output_buffer == NULL) {
  1913.     gvw->ghostview.output_buffer = XtMalloc(GV_BUFSIZ);
  1914.     }
  1915.     GLInsert(gvw);
  1916.     /*
  1917.     ** Fake a completed I/O so Output will get called to queue the first I/O.
  1918.     */
  1919.     gvw->ghostview.interpreter_output_iosb[0] = SS$_NORMAL;
  1920.     gvw->ghostview.interpreter_output_iosb[1] = -1;
  1921.     IOComplete();
  1922. }
  1923.  
  1924. /* Stop the interperter, if present, and remove any Input sources. */
  1925. /* Also reset the busy state. */
  1926. static void
  1927. StopInterpreter(w)
  1928.     Widget w;
  1929. {
  1930.     int ret;
  1931.     
  1932.     GhostviewWidget gvw = (GhostviewWidget) w;
  1933.     if (gvw->ghostview.interpreter_pid >= 0) {
  1934.     ret = sys$delprc(&gvw->ghostview.interpreter_pid, 0);
  1935.     if(ret != SS$_NORMAL && ret != SS$_NONEXPR)lib$signal(ret, 0, 0);
  1936.     gvw->ghostview.interpreter_pid = -1;
  1937.     }
  1938.     if (gvw->ghostview.interpreter_input >= 0) {
  1939.     (void) sys$dassgn(gvw->ghostview.interpreter_input);
  1940.     gvw->ghostview.interpreter_input = -1;
  1941.     while (gvw->ghostview.ps_input) {
  1942.         struct record_list *ps_old = gvw->ghostview.ps_input;
  1943.         gvw->ghostview.ps_input = ps_old->next;
  1944.         if (ps_old->close) fclose(ps_old->fp);
  1945.         XtFree((char *)ps_old);
  1946.     }
  1947.     }
  1948.     if (gvw->ghostview.interpreter_output >= 0) {
  1949.     (void) sys$dassgn(gvw->ghostview.interpreter_output);
  1950.     gvw->ghostview.interpreter_output = -1;
  1951.     }
  1952.     gvw->ghostview.busy = False;
  1953.     XDefineCursor(XtDisplay(gvw), XtWindow(gvw), gvw->ghostview.cursor);
  1954.     GLRemove(gvw);
  1955. }
  1956.  
  1957. /* GhostviewSendPS:
  1958.  *   Queue a portion of a PostScript file for output to ghostscript.
  1959.  *   fp: FILE * of the file in question.  NOTE: if you have several
  1960.  *   Ghostview widgets reading from the same file.  You must open
  1961.  *   a unique FILE * for each widget.
  1962.  *   SendPS does not actually send the PostScript, it merely queues it
  1963.  *   for output.
  1964.  *   begin: position in file (returned from ftell()) to start.
  1965.  *   len:   number of bytes to write.
  1966.  *
  1967.  *   If an interpreter is not running, nothing is queued and
  1968.  *   False is returned.
  1969.  */
  1970. Boolean
  1971. GhostviewSendPS(w, fp, begin, len, close)
  1972.     Widget w;
  1973.     FILE *fp;
  1974.     long begin;
  1975.     unsigned int len;
  1976.     Bool close;
  1977. {
  1978.     GhostviewWidget gvw = (GhostviewWidget) w;
  1979.     struct record_list *ps_new;
  1980.  
  1981.     if (gvw->ghostview.interpreter_input < 0) return False;
  1982.     if(len != 0){
  1983.     ps_new = (struct record_list *) XtMalloc(sizeof (struct record_list));
  1984.     ps_new->fp = fp;
  1985.     ps_new->begin = begin;
  1986.     ps_new->len = len;
  1987.     ps_new->seek_needed = True;
  1988.     ps_new->close = close;
  1989.     ps_new->next = NULL;
  1990.  
  1991.     if (gvw->ghostview.input_buffer == NULL) {
  1992.         gvw->ghostview.input_buffer = XtMalloc(GV_BUFSIZ);
  1993.     }
  1994.  
  1995.     if (gvw->ghostview.ps_input == NULL) {
  1996.         gvw->ghostview.bytes_left = len;
  1997.         gvw->ghostview.ps_input = ps_new;
  1998.         /*
  1999.         ** Fake a completed I/O so Input will get called to queue the
  2000.         ** first I/O.
  2001.         */
  2002.         gvw->ghostview.interpreter_input_iosb[0] = SS$_NORMAL;
  2003.         gvw->ghostview.interpreter_input_iosb[1] = -1;
  2004.         IOComplete();
  2005.     } else {
  2006.         struct record_list *p = gvw->ghostview.ps_input;
  2007.         while (p->next != NULL) {
  2008.         p = p->next;
  2009.         }
  2010.         p->next = ps_new;
  2011.     }
  2012.     }
  2013.     return True;
  2014. }
  2015. #endif /* VMS */
  2016.